class Path {

  ; 设置当前目录
  static Workdir := '' || A_ScriptDir
  static Sep := '/'

  ; param: separator | paths
  ; ret:   path --> string
  ; desc:  Connect paths using separator
  static Join(sep, params*) {
    for index, param in params
      str .= param . sep
    return SubStr(str, 1, -StrLen(sep))
  }

  ; param: paths
  ; ret:   absolute path --> string
  ; desc:  convert to absolute path
  static Resolve(params*) {
    len := params.Length
    ; 单参，默认以当前目录为基准
    if !len || (len == 1 && (params[1] == '' || params[1] == '.')) { ; 无参，参数为空值及.返回当前目录
      return StrReplace(Path.Workdir, '\', Path.Sep)
    } else if (len == 1) {
      if (params[1] ~= '^\.[\\/]') { ; ./开头，直接去除开头./，再规范化后即可返回
        ; 去除开头的./
        _path := Path.Workdir SubStr(params[1], 2)
        ; 统一分隔符
        _path := StrReplace(_path, '/', '\')
        ; 去除末尾 \
        _path := RTrim(_path, '\')
        ; 规范化
        _path := StrReplace(_path, '\', Path.Sep)
        return _path
      }
      else if (params[1] ~= '^\.\.[\\/]') { ; ../ 开头的单参，连接目录路径后放行
        params[1] := Path.Workdir params[1]
      }
      else if (params[1] ~= '^\.\.$') { ; .. 添加sep后放行
        params[1] := Path.Workdir params[1] Path.Sep
      }
      else { ;无法处理
        return StrReplace(Path.Workdir, '\', Path.Sep)
      }
    }
    ; 多参
    for index, param in params {
      if !param ; 跳过空值
        continue
      if param ~= '^[\\/]' { ; 如果以分隔符开头，则清除先前的路径
        _path := param
      }
      else if (index == 1) ; 第一个参数，不加sep
        _path .= param
      else ; 添加分隔符
        _path .= '\' param
    }
    ; 统一分隔符
    _path := StrReplace(_path, '/', '\')
    ; 替换 ..
    while (_path ~= 'i)\.\.') {
      ; 找到..位置
      subStart := InStr(_path, '..')
      ; 找到..前第二个\位置
      subEnd := InStr(_path, '\', , -(StrLen(_path) - subStart) - 3)
      ; 裁剪出待替换字串
      sub := SubStr(_path, subEnd, subStart - subEnd + 2)
      ; MsgBox subStart '--' subEnd '`n' sub '`n' _path
      _path := StrReplace(_path, sub, '')
      if !sub
        throw Error('invalid param! current path--> ' _path)
    }
    ; 替换 .\
    _path := StrReplace(_path, '.\', '')
    ; 去除末尾 \
    _path := RTrim(_path, '\')
    ; 规范化
    _path := StrReplace(_path, '\', Path.Sep)
    ; 判断是否带盘符，无则添加当前盘符
    if _path ~= 'i)^\w:[\\/]'
      return _path
    ; 判断是否添加全路径
    else if _path ~= '^[\\/]' {
      drive := SubStr(Path.Workdir, 1, 1)
      return drive ':' _path
    }
    else {
      scriptDir := StrReplace(Path.Workdir, '/', Path.Sep)
      scriptDir := StrReplace(Path.Workdir, '\', Path.Sep)
      return scriptDir Path.sep _path
    }
  }
}
if A_LineFile == A_ScriptFullPath {
  MsgBox Path.Resolve() ; C:\Users\u\Desktop\AhkPlanE\utils
  MsgBox Path.Resolve('.') ; C:\Users\u\Desktop\AhkPlanE\utils
  MsgBox Path.Resolve('./') ; C:\Users\u\Desktop\AhkPlanE\utils
  MsgBox Path.Resolve('..') ; C:\Users\u\Desktop\AhkPlanE
  MsgBox Path.Resolve('../') ; C:\Users\u\Desktop\AhkPlanE
  MsgBox Path.Resolve('./abc\') ; C:\Users\u\Desktop\AhkPlanE\utils\abc
  MsgBox Path.Resolve('..\abc\..\def')  ; C:\Users\u\Desktop\AhkPlanE\def
  MsgBox Path.Resolve('C:\Users\u\Desktop\AhkPlanE', '../') ; C:\Users\u\Desktop
  MsgBox Path.Resolve('C:\Users\u\Desktop\AhkPlanE', '../res/light/../abc') ; C:\Users\u\Desktop\res\abc
  MsgBox Path.Resolve('/ic', '../a/b', '.\d') ; C:\a\b\d
  MsgBox Path.Resolve('abc', '/ic', '../a/b', '.\d') ; C:\a\b\d
}